﻿using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Lokad.CodeDsl
{
    public sealed class MessageGenerator : IGenerateCode
    {
       public string ClassNameTemplate { get; set; }
        public string MemberTemplate { get; set; }
        public string PrivateCtorTemplate { get; set; }
        public string Namespace { get; set; }
        public string Region { get; set; }
        public string GenerateInterfaceForEntityWithModifiers { get; set; }
        public string TemplateForInterfaceName { get; set; }
        public string TemplateForInterfaceMember { get; set; }

        public MessageGenerator()
		{
		    ClassNameTemplate = @"
public sealed class {0}";

		    MemberTemplate = @"public {0} {1}";
		    PrivateCtorTemplate = @"
public {0} () {{}}";
		    TemplateForInterfaceName = "public interface I{0}";
		    TemplateForInterfaceMember = "void When({0} c)";
		    GenerateInterfaceForEntityWithModifiers = "none";
		    //
		}

        public void Generate(Context context, IndentedTextWriter outer)
		{
			var writer = new CodeWriter(outer);
            if (!string.IsNullOrEmpty(Namespace))
            {
                writer.WriteLine("namespace {0}", Namespace);
                writer.WriteLine("{");
            }
            writer.Indent += 1;

            if (!string.IsNullOrEmpty(Region))
            {
                writer.WriteLine("#region {0}", Region);
            }

			WriteContext(writer, context);


            if (!string.IsNullOrEmpty(Region))
            {
                writer.WriteLine("#endregion");
            }

            writer.Indent -= 1;

            if (!string.IsNullOrEmpty(Namespace))
            {
                writer.WriteLine("}");
            }
		}

        private void WriteContext(CodeWriter writer, Context context)
	    {
	        foreach (var contract in context.Contracts)
	        {
	            writer.Write(ClassNameTemplate, contract.Name);
	            
	            if (contract.Modifiers.Any())
	            {
                    writer.Write(" : {0}", string.Join(", ", contract.Modifiers.Select(s => s.Interface).ToArray()));
	            }
	            writer.WriteLine();

	            writer.WriteLine("{");
	            writer.Indent += 1;

	            if (contract.Members.Count > 0)
	            {
	                WriteMembers(contract, writer);
	                writer.WriteLine(PrivateCtorTemplate, contract.Name);
	                writer.Write("public {0} (", contract.Name);
	                WriteParameters(contract, writer);
	                writer.WriteLine(")");
	                writer.WriteLine("{");

	                writer.Indent += 1;
	                WriteAssignments(contract, writer);
	                writer.Indent -= 1;

	                writer.WriteLine("}");
					
	            }


	            writer.Indent -= 1;
	            writer.WriteLine("}");
	        }


            var split = (GenerateInterfaceForEntityWithModifiers ?? "")
                .Split(new[]{','}, StringSplitOptions.RemoveEmptyEntries);
            foreach (var entity in context.Entities)
            {
                if ((entity.Name??"null") == "null")
                    continue;
                var matches = entity.Messages.Where(m => m.Modifiers.Select(s =>s.Identifier).Intersect(split).Any()).ToList();
                if (matches.Any())
                {
                    writer.WriteLine();
                    writer.WriteLine(TemplateForInterfaceName, entity.Name);
                    writer.WriteLine("{");
                    writer.Indent += 1;
                    foreach (var contract in matches)
                    {
                        writer.WriteLine(TemplateForInterfaceMember, contract.Name);
                    }
                    writer.Indent -= 1;
                    writer.WriteLine("}");
                }
            }
	    }

	    void WriteMembers(Message message, CodeWriter writer)
		{
			var idx = 1;
			foreach (var member in message.Members)
			{
                writer.WriteLine(MemberTemplate, member.Type, GeneratorUtil.MemberCase(member.Name) + " { get; private set; }");

				
				idx += 1;
			}
		}
        void WriteParameters(Message message, CodeWriter writer)
		{
			var first = true;
			foreach (var member in message.Members)
			{
				if (first)
				{
					first = false;
				}
				else
				{
					writer.Write(", ");
				}
				writer.Write("{0} {1}", member.Type, GeneratorUtil.ParameterCase(member.Name));
			}
		}

        void WriteAssignments(Message message, CodeWriter writer)
		{
			foreach (var member in message.Members)
			{
				writer.WriteLine("{0} = {1};", GeneratorUtil.MemberCase(member.Name), GeneratorUtil.ParameterCase(member.Name));
			}
		}
	}

}
